﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DeepSpace.Geometry;

namespace DeepSpace 
{
    public class KDNode
    {
        public enum Axis
        {
            XAxis,
            YAxis,
            ZAxis,
        }

        const int MaxDepth = 8;
        const int MaxSize = 1024;

        private BoundingBox bounding;
        private Axis axis;
        private KDNode left, right;
        private List<Intersectable> geometries = new List<Intersectable>();

        public KDNode(BoundingBox boundingBox, Axis axis)
        {
            this.bounding = boundingBox;
            this.axis = axis;
        }

        public void Add(IEnumerable<Intersectable> newGeometries)
        {
            AddInernal(newGeometries, 0);
        }

        private void AddInernal(IEnumerable<Intersectable> newGeometries, int depth)
        {
            // Add all geometries to the geometry list
            geometries.AddRange(newGeometries.Where(g => g.IsInside(bounding)));

            if (geometries.Count <= MaxSize || depth > MaxDepth)
            {
                return;
            }

            BoundingBox leftBounding = new BoundingBox(bounding);
            BoundingBox rightBounding = new BoundingBox(bounding);

            switch (axis)
            {
                case Axis.XAxis:
                    var splitX = (bounding.MinPoint.X + bounding.MaxPoint.X) / 2;
                    leftBounding.MaxPoint.X = splitX;
                    rightBounding.MinPoint.X = splitX;
                    break;

                case Axis.YAxis:
                    var splitY = (bounding.MinPoint.Y + bounding.MaxPoint.Y) / 2;
                    leftBounding.MaxPoint.Y = splitY;
                    rightBounding.MinPoint.Y = splitY;
                    break;

                case Axis.ZAxis:
                    var splitZ = (bounding.MinPoint.Z + bounding.MaxPoint.Z) / 2;
                    leftBounding.MaxPoint.Z = splitZ;
                    rightBounding.MinPoint.Z = splitZ;
                    break;
            }

            Axis nextAxis = axis == Axis.ZAxis ? Axis.XAxis : axis + 1;

            // do we really need to copy all geometries to the children?
            left = new KDNode(leftBounding, nextAxis);
            left.AddInernal(geometries, depth + 1);

            right = new KDNode(rightBounding, nextAxis);
            right.AddInernal(geometries, depth + 1);
        }
    }
}
